#pragma once
/* Copyright (C) 2009 Vlad Roubtsov. All rights reserved. */

#include <memory>
using std::auto_ptr;
#include <string>
using std::string;
#include <vector>
using std::vector;

#include "vr/deject/component_desc.hpp"
#include "vr/deject/component.hpp"
#include "vr/deject/initializable.hpp"
#include "vr/deject/startable.hpp"
#include "vr/deject/factory/dependency.hpp"
#include "vr/exception/exceptions.hpp"
#include "vr/util/types.hpp"
#include "vr/util/utility.hpp"

//----------------------------------------------------------------------------
namespace vr
{
namespace deject
{
namespace impl
{

} // end of 'impl'
/**
 * NOTE container is not a component by design
 */
class container: public startable,
                        noncopyable
{
    public: // ...............................................................

        static auto_ptr<container> create (const string & name, const cfg_type & cfg);

        ~container ();

        // startable:

        void start ();
        void stop () throw (); // stronger [nothrow] guarantee than component::stop()


        template<typename T>
        T & get (const component_id_type & ID) const
        {
            const component_map::const_iterator i = m_components.find (ID);
            if (UNLIKELY (i == m_components.end ()))
                __VR_THROW_X (std::invalid_argument, "no component mapping for [" + ID + "]");

            const ctx * const c = i->second;
            VR_SASSERT (c);
            VR_SASSERT (c->m_component);

            T * const cT = dynamic_cast<T *> (c->m_component);
            if (UNLIKELY (cT == 0))
                __VR_THROW_X (std::invalid_argument, "component [" + ID + "] of type [" + utility::as_class_name (typeid (* c->m_component).name ()) + "] cannot be cast to [" + utility::as_class_name (typeid (T).name ()) + "]");

            return * cT;
        }


    private: // ..............................................................

        struct ctx: noncopyable
        {
            const component_desc * m_descriptor; // valid ONLY during container construction stage
            int_fast16_t m_visit_state; // 0 -> not visited, 1 -> visit started, 2 -> visit complete

            component_base * m_component; // ownership pointer [valid after initialization

            component_id_type m_ID; // copy of info in m_descriptor retained post-container construction
            string m_implID; // copy of info in m_descriptor retained post-container construction

            initializable * m_initializable; // interface handle
            startable * m_startable; // interface handle

            ctx () :
                m_descriptor (0),
                m_visit_state (0),
                m_component (0),
                m_initializable (0),
                m_startable (0)
            {
            }

            ~ctx (); // defined in .cpp

        }; // end of nested class


        typedef ptr_map<component_id_type, ctx> component_map;


        container (const string & name, component_map & components, const vector<ctx *> & ordering);

        static void traverse (const component_id_type & node, const cfg_type & cfg, component_map & mapping, vector<ctx *> & ordering);
        static void bind_dependencies (const ctx & node_ctx, const component_map & mapping, ptr_vector<factory::dependency> & out);
        static bool destroy (const component_id_type & ID, const string & implID, component_base * const c);


        const string m_name;
        component_map m_components;
        vector<ctx *> m_ordering; // of initialization

}; // end of class

} // end of 'deject'
} // end of namespace
//----------------------------------------------------------------------------
